package cn.com.cis.web.actions;

import cn.com.cis.domain.Role;
import cn.com.cis.domain.User;
import cn.com.cis.plugins.mybatis.PageInfo;
import cn.com.cis.service.PrivilegeService;
import cn.com.cis.service.RoleService;
import cn.com.cis.service.UserService;
import cn.com.cis.utils.security.PasswordHash;
import cn.com.cis.utils.tree.PermissionArrayManager;
import cn.com.cis.utils.tree.PermissionWithProperties;
import cn.com.cis.web.bean.UserActionBean;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributesModelMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Controller
public class UserController {

    private static final Logger log = LoggerFactory.getLogger(UserController.class);

    @Autowired
    private UserService userService;
    
    @Autowired
    private SecurityManager securityManager;
    
    @Autowired
    private PrivilegeService privilegeService;
    
    @Autowired
    private RoleService roleService;
    
    private void setNavigateBar(ModelAndView mv){
    	mv.addObject("icon", "icon-list");
        mv.addObject("title", "用户管理");
    }
    
    @RequestMapping(value = "/login", method = {RequestMethod.GET, RequestMethod.HEAD})
    public ModelAndView login() {
    	ModelAndView mv = new ModelAndView("login");
    	setNavigateBar(mv);
    	
    	if(haveLogin()){
    		mv.setViewName("redirect:/main");
        }
    	
        return mv;
    }
    
    private boolean haveLogin(){
    	SecurityUtils.setSecurityManager(securityManager);
    	Subject currentUser = SecurityUtils.getSubject();
    	
    	if(currentUser.isAuthenticated()){
    		return true;
    	}
    	
    	return false;
    }

    @RequestMapping(value = "/login",method = RequestMethod.POST)
    public ModelAndView login(String username, String password, HttpServletRequest httpServletRequest, RedirectAttributesModelMap modelMap) {
        ModelAndView mv = new ModelAndView();
        setNavigateBar(mv);
        
        if (this.checkParams(new String[]{username, password})) {
            if (true == authWithShiro(username, password)) {
                mv.setViewName("redirect:/main");
                HttpSession session = httpServletRequest.getSession();
                User user = userService.selectUserByUsername(username);
                session.setAttribute("user", user);
                session.setAttribute("validUrls", getCurrentUserPermissions(user));
                modelMap.addFlashAttribute("icon", "icon-home");
                modelMap.addFlashAttribute("title", "主页");
                modelMap.addFlashAttribute("desc", "服务器状态、服务器日志、服务器负载");
            } else {
                mv.setViewName("login");
                mv.addObject("error","用户名或密码错误！");
            }
        } else {
            mv.setViewName("login");
            mv.addObject("error","用户名与密码不能为空！");
        }
        return mv;
    }
    
    private List<String> getCurrentUserPermissions(User currentUser){
    	PermissionArrayManager pam = new PermissionArrayManager();
        pam.setPrivilegeService(privilegeService);
        pam.setRoleService(roleService);
        pam.setUserService(userService);
        pam.isSetPermissionsForRole(false);
        pam.setUser(currentUser);
    	return pam.getUrls();
    }
    
    private boolean authWithShiro(String username, String password) {
    	
    	User user = userService.selectUserByUsername(username);
    	
    	if(null == user || !user.isEnabled()){
    		return false;
    	}
    	
    	SecurityUtils.setSecurityManager(securityManager);
    	Subject currentUser = SecurityUtils.getSubject();
    	
    	boolean isLoginSuccess = false;
    	
    	// let's login the current user so we can check against roles and permissions:
		UsernamePasswordToken token = new UsernamePasswordToken(username, password);

		try {
			currentUser.login(token);
			isLoginSuccess = true;
		} catch (UnknownAccountException e) {
			// e.printStackTrace();
			// use for audit
			log.info("There is no user with username of " + token.getPrincipal());
		} catch (IncorrectCredentialsException e) {
			// e.printStackTrace();
			// use for audit
			log.info("Password for account " + token.getPrincipal() + " was incorrect!");
		} catch (LockedAccountException e) {
			// e.printStackTrace();
			// use for audit
			log.info("The account for username " + token.getPrincipal() + " is locked.  " + "Please contact your administrator to unlock it.");
		} catch (AuthenticationException e) {
			//e.printStackTrace();
			// use for audit
			log.info("user " + token.getPrincipal() + " try to login but failed for some reason.");
		}
        
		return isLoginSuccess;
	}

    private boolean checkParams(String[] params) {
        for (String param : params) {
            if (param == "" || param == null || param.isEmpty()) {
                return false;
            }
        }
        
        return true;
    }

    @RequestMapping(value = "/logout", method = RequestMethod.GET)
    public ModelAndView logout(HttpServletRequest httpServletRequest) {
        HttpSession session = httpServletRequest.getSession();
        session.removeAttribute("user");
        
        SecurityUtils.setSecurityManager(securityManager);
    	Subject currentUser = SecurityUtils.getSubject();
    	if (currentUser.isAuthenticated()) {
    		currentUser.logout();
    	}
        
    	ModelAndView mv = new ModelAndView("redirect:/login");
    	setNavigateBar(mv);
        return mv;
    }

    @RequiresPermissions("配置管理:用户管理:添加用户")
    @RequestMapping(value = "/user/add", method = RequestMethod.GET)
    public ModelAndView addView() {
        ModelAndView mav = new ModelAndView("/user/add");
        setNavigateBar(mav);
        
        List<Role> allRoles = roleService.selectRoles();
        mav.addObject("allRoles", allRoles);
        
        return mav;
    }

    @RequiresPermissions("配置管理:用户管理:添加用户")
    @RequestMapping(value = "/user/add", method = RequestMethod.POST)
    @ResponseBody
    public String save(UserActionBean userbean) {
    	
    	// 用户输入校验
    	if (null == userbean.getUsername() || !userbean.getUsername().matches("[a-zA-Z]{2,}")) {
    		return "用户名不合法！";
		}
    	
    	if(null != userService.selectUserByUsername(userbean.getUsername())){
    		return "用户名已存在！";
    	}
    	
    	if (null == userbean.getPassword() || userbean.getPassword().length() < 6) {
			return "密码设置不符合要求！";
		}
    	
    	if(null == userbean.getEmail() || !userbean.getEmail().matches(".+@.+\\..{2,}")){
    		return "邮箱格式不正确！";
    	}
    	
    	if(null == userbean.getPhone() || !userbean.getPhone().matches("\\d{11}")){
    		return "电话格式不正确！";
    	}
    	// 用户输入校验
    	
        try {
			addUser(userbean);
		} catch (Exception e) {
			e.printStackTrace();
			return e.getMessage();
		}
        
        return "success";
    }
    
    // TODO should be in one transaction
    private void addUser(UserActionBean userbean){
    	User user = new User();
        user.setUsername(userbean.getUsername());
        user.setPassword(userbean.getPassword());
        user.setEmail(userbean.getEmail());
        user.setPhone(userbean.getPhone());
        user.setEnabled(userbean.isEnabled());
        user.setRemark(userbean.getRemark());
        userService.insertUser(user);
        
        if(null != userbean.getRolesAssignToUser()){
	        for(String roleId : userbean.getRolesAssignToUser().split(",")){
	        	if(!"".equals(roleId)){
	        		userService.addRole(user.getId(), roleId);
	        	}
	        }
        }
    }

    @RequiresPermissions("配置管理:用户管理:查看用户列表")
    @RequestMapping(value = "/user/list/{page}", method = RequestMethod.GET)
    public ModelAndView list(@PathVariable("page") int page, HttpServletRequest request) {
        ModelAndView mav = new ModelAndView("user/list");
        setNavigateBar(mav);
        
        List<User> userList = userService.selectAllUser(page);
        PageInfo<User> pageInfo = new PageInfo<User>(userList,10);
        
        Map<Integer, String> userRoles = new HashMap<Integer, String>();
        for(User user : userList){
        	String roleString = "";
        	
        	List<Role> roles = userService.selectUserRoles(user.getId());
        	for(Role role : roles){
        		roleString += role.getRoleName() + ",";
        	}
        	
        	if(roleString.length() > 0){
        		roleString = roleString.substring(0, roleString.length() - 1);
        	}
        	
        	userRoles.put(user.getId(), roleString);
        }
        
		mav.addObject("userList", userList);
		mav.addObject("pageInfo", pageInfo);
		mav.addObject("userRoles", userRoles);
		mav.addObject("error", request.getParameter("error"));
		mav.addObject("msg", request.getParameter("msg"));
        
        return mav;
    }

    @RequiresPermissions("配置管理:用户管理:修改用户基本信息")
    @RequestMapping(value = "/user/edit", method = RequestMethod.GET)
    public ModelAndView editView(int id) {
        ModelAndView mav = new ModelAndView("user/edit");
        setNavigateBar(mav);
        
        User user = userService.selectUserById(id);
        List<Role> rolesUserHave = roleService.selectRolesByUserId(id);
        List<Role> allRoles = roleService.selectRoles();
        
        mav.addObject("user", user);
        mav.addObject("rolesUserHave", rolesUserHave);
        mav.addObject("allRoles", allRoles);
        
        return mav;
    }

    @RequiresPermissions("配置管理:用户管理:修改用户基本信息")
    @RequestMapping(value = "/user/edit", method = RequestMethod.POST)
    @ResponseBody
    public String update(UserActionBean userbean) {
    	
    	// 动作合法性验证
    	User userValid = userService.selectUserById(userbean.getId());
    	if (null != userValid.getUsername() && "admin".equals(userValid.getUsername())) {
    		return "admin用户不允许被修改！";
		}
		// 动作合法性验证
    	
    	// 用户输入校验
    	if (null == userbean.getUsername() || !userbean.getUsername().matches("[a-zA-Z]{2,}")) {
    		return "用户名不合法！";
		}
    	
    	if (null != userService.selectUserByUsername(userbean.getUsername())) {
			String oldUserName = userService.selectUserByUsername(userbean.getUsername()).getUsername();
			if(!userbean.getUsername().equals(oldUserName)){
				return "用户名已存在！";
			}
		}
    	
    	if (null != userbean.getPassword() && userbean.getPassword().length() < 6 && userbean.getPassword().length() > 0) {
			return "密码设置不符合要求！";
		}
    	
    	if(null == userbean.getEmail() || !userbean.getEmail().matches(".+@.+\\..{2,}")){
    		return "邮箱格式不正确！";
    	}
    	
    	if(null == userbean.getPhone() || !userbean.getPhone().matches("\\d{11}")){
    		return "电话格式不正确！";
    	}
    	// 用户输入校验
    	
        try {
        	updateUser(userbean);
		} catch (Exception e) {
			e.printStackTrace();
			return e.getMessage();
		}
        
        return "success";
    }
    
    // TODO should be in one transaction
    private void updateUser(UserActionBean userbean){
    	User user = userService.selectUserById(userbean.getId());
    	user.setUsername(userbean.getUsername());
        user.setEmail(userbean.getEmail());
        user.setEnabled(userbean.isEnabled());
        user.setId(userbean.getId());
        user.setPhone(userbean.getPhone());
        user.setRemark(userbean.getRemark());
        
        if(userbean.getPassword() == null || "".equals(userbean.getPassword())){
        	userService.updateUser(user);
        } else {
        	user.setPassword(userbean.getPassword());
			userService.updateUserAndPassword(user);
		}
        
        userService.clearRoles(userbean.getId());
        if(null != userbean.getRolesAssignToUser()){
	        for(String roleId : userbean.getRolesAssignToUser().split(",")){
	        	if(!"".equals(roleId)){
	        		userService.addRole(userbean.getId(), roleId);
	        	}
	        }
        }
    }
    
    @RequiresPermissions("配置管理:用户管理:修改用户权限")
    @RequestMapping(value = "/user/editPrivilege", method = RequestMethod.GET)
    public ModelAndView updateUserPrivilege(int id) {
    	ModelAndView mav = new ModelAndView("user/editPrivilege");
    	setNavigateBar(mav);
    	
        User user = userService.selectUserById(id);
        mav.addObject("user", user);
        
        PermissionArrayManager pam = new PermissionArrayManager();
        pam.setPrivilegeService(privilegeService);
        pam.setRoleService(roleService);
        pam.setUserService(userService);
        pam.isSetPermissionsForRole(false);
        pam.setUser(userService.selectUserById(id));
        
		Map<String, List<PermissionWithProperties>> permissionArray = pam.getPermissionArray();
		mav.addObject("permissionArray", permissionArray);
        
        return mav;
    }
    
    @RequiresPermissions("配置管理:用户管理:修改用户权限")
    @RequestMapping(value = "/user/savePrivilege", method = RequestMethod.POST)
    @ResponseBody
    // TODO should be in one transaction
    public String saveUserPrivilege(int id, String code) throws UnsupportedEncodingException {
    	
    	try {
			userService.clearPermissions(id);
			if (null != code) {
				for (String scode : URLDecoder.decode(code, "UTF-8").split(";")) {
					userService.addPermissionForUser(id, scode);
				}
			} 
		} catch (Exception e) {
			e.printStackTrace();
			return e.getMessage();
		}
    	
		return "success";
    }

    @RequestMapping(value = "/user/editpwd", method = RequestMethod.GET)
    public ModelAndView editpwdView() {
        ModelAndView mav = new ModelAndView("user/editpwd");
        setNavigateBar(mav);
        return mav;
    }

    @RequestMapping(value = "/user/editpwd", method = RequestMethod.POST)
    @ResponseBody
    public String updatepwd(UserActionBean userbean, HttpServletRequest httpServletRequest) {
    	
    	if(!oldPasswordValid(userbean.getOldpassword(), httpServletRequest)){
    		return "密码不正确！";
    	}
    	
    	if(null == userbean.getRepassword() || userbean.getRepassword().length() < 6){
    		return "密码长度必须大于6！";
    	}
    	
        try {
			User user = userService.selectUserById(userbean.getId());
			user.setPassword(userbean.getRepassword());
			userService.updateUserAndPassword(user);
		} catch (Exception e) {
			e.printStackTrace();
			return e.getMessage();
		}
        
		return "success";
    }
    
    private boolean oldPasswordValid(String oldpassword, HttpServletRequest httpServletRequest) {
    	
    	HttpSession session = httpServletRequest.getSession();
    	User user = (User) session.getAttribute("user");
    	
    	char[] needValidPassword = oldpassword.toCharArray();
    	String correctHash = PasswordHash.PBKDF2_ITERATIONS + ":" + user.getSalt() + ":" + user.getPassword();
    	
		return PasswordHash.validatePassword(needValidPassword, correctHash);
	}

	@RequestMapping(value = "/user/editProfile", method = RequestMethod.GET)
    public ModelAndView editProfile(HttpServletRequest httpServletRequest) {
        ModelAndView mav = new ModelAndView("user/editProfile");
        setNavigateBar(mav);
        
        HttpSession session = httpServletRequest.getSession();
        User currentUser = (User)session.getAttribute("user");

        User updatedUser = userService.selectUserById(currentUser.getId());
        session.setAttribute("user", updatedUser);
        
        return mav;
    }
    
    @RequestMapping(value = "/user/editProfile", method = RequestMethod.POST)
    @ResponseBody
    public String editProfile(UserActionBean userActionBean, HttpServletRequest httpServletRequest, RedirectAttributesModelMap modelMap) {
        
		// 用户输入校验
		if (null == userActionBean.getEmail() || !userActionBean.getEmail().matches(".+@.+\\..{2,}")) {
			return "邮箱格式不正确！";
		}

		if (null == userActionBean.getPhone() || !userActionBean.getPhone().matches("\\d{11}")) {
			return "电话格式不正确！";
		}
		// 用户输入校验
        
        HttpSession session = httpServletRequest.getSession();
        User currentUser = (User)session.getAttribute("user");
        
        User user = new User();
        user.setId(currentUser.getId());
        user.setEmail(userActionBean.getEmail());
        user.setPhone(userActionBean.getPhone());
        
        user.setUsername(currentUser.getUsername());
        user.setRemark(currentUser.getRemark());
        user.setEnabled(currentUser.isEnabled());
        
        userService.updateUser(user);
        
        return "success";
    }

    @RequiresPermissions("配置管理:用户管理:删除用户")
    @RequestMapping(value = "/user/del", method = RequestMethod.GET)
    public ModelAndView del(int id) {
        ModelAndView mav = new ModelAndView("redirect:/user/list/1");
        setNavigateBar(mav);
        
		// 动作合法性验证
		User userValid = userService.selectUserById(id);
		if (null != userValid.getUsername() && "admin".equals(userValid.getUsername())) {
			mav.addObject("error", "不允许操作admin用户！");
			return mav;
		}
		// 动作合法性验证
        
        userService.deleteUser(id);
        
        mav.addObject("msg", "删除成功！");
        return mav;
    }

}
